package com.mlming.simpledb.backend.dm.page;

import com.mlming.simpledb.backend.dm.pageCache.PageCache;
import com.mlming.simpledb.backend.utils.RandomUtil;

import java.util.Arrays;

/**
 * 数据库文件的第一页，通常用作一些特殊用途，比如存储一些元数据，用来启动检查什么的。
 * 本项目的第一页，只是用来做启动检查。
 * 具体的原理是，
 * 在每次数据库启动时，会生成一串随机字节，存储在 100 ~ 107 字节。
 * 在数据库正常关闭时，会将这串字节，拷贝到第一页的 108 ~ 115 字节。
 * 这样数据库在每次启动时，就会检查第一页两处的字节是否相同，以此来判断上一次是否正常关闭。
 * 如果是异常关闭，就需要执行数据的恢复流程。
 *
 * ValidCheck, 所以下面以VC表示
 *
 * 以下类, 就是根据上述的定义,
 * 对 第一页的Page对象 的 实际存储数据byte[] data进行一个设置与操作
 */
public class PageOneDataHandler {
    // 定义参数:
    private static final int OF_VC = 100;
    private static final int LEN_VC = 8;

    // 打开数据库时会被调用, 获取第一页Page对象的byte[] data
    public static byte[] InitData() {
        // 定义一个byte[]用于存放数据, 页大小就是我们之前说的PageCache.PAGE_SIZE
        byte[] data = new byte[PageCache.PAGE_SIZE];
        // 根据上述定义, 要生成一串随机字节，存储在 100 ~ 107 字节。
        setVcOpen(data);
        // 返回data数据, 让外面调用者来设置到Page里面去
        return data;
    }
    // 获取随机字节并存入data中
    private static void setVcOpen(byte[] data) {
        // 通过RandomUtil生成一个长度为LEN_VC的随机字节
        // 随机字节从0开始, 读出LEN_VC, 存到data的OF_VC开始的位置
        System.arraycopy(RandomUtil.randomBytes(LEN_VC), 0, data, OF_VC, LEN_VC);
    }
    // 获取随机字节并存入指定Page里的data中
    public static void setVcOpen(Page page) {
        page.setDirty(true);// 因为修改了, 所以要置为脏页
        setVcOpen(page.getData());
    }

    // 关闭数据库时会被调用, 用于会将100-107这段字节，拷贝到第一页的 108 ~ 115 字节。
    public static void setVcClose(Page pg) {
        pg.setDirty(true);
        setVcClose(pg.getData());
    }

    private static void setVcClose(byte[] data) {
        System.arraycopy(data, OF_VC, data, OF_VC+LEN_VC, LEN_VC);
    }

    // 数据库在每次启动时，就会检查第一页两处的字节是否相同，以此来判断上一次是否正常关闭。
    public static boolean checkVc(Page pg) {
        return checkVc(pg.getData());
    }

    private static boolean checkVc(byte[] data) {
        return Arrays.equals(Arrays.copyOfRange(data, OF_VC, OF_VC+LEN_VC), Arrays.copyOfRange(data, OF_VC+LEN_VC, OF_VC+2*LEN_VC));
    }
}
